﻿#region Copyright (c) 2000-2008 Developer Express Inc.
/*
{*******************************************************************}
{                                                                   }
{       Developer Express .NET Component Library                    }
{       AgDataGrid                                 }
{                                                                   }
{       Copyright (c) 2000-2008 Developer Express Inc.              }
{       ALL RIGHTS RESERVED                                         }
{                                                                   }
{   The entire contents of this file is protected by U.S. and       }
{   International Copyright Laws. Unauthorized reproduction,        }
{   reverse-engineering, and distribution of all or any portion of  }
{   the code contained in this file is strictly prohibited and may  }
{   result in severe civil and criminal penalties and will be       }
{   prosecuted to the maximum extent possible under the law.        }
{                                                                   }
{   RESTRICTIONS                                                    }
{                                                                   }
{   THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES           }
{   ARE CONFIDENTIAL AND PROPRIETARY TRADE                          }
{   SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS   }
{   LICENSED TO DISTRIBUTE THE PRODUCT AND ALL ACCOMPANYING .NET    }
{   CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY.                 }
{                                                                   }
{   THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED      }
{   FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE        }
{   COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE       }
{   AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT  }
{   AND PERMISSION FROM DEVELOPER EXPRESS INC.                      }
{                                                                   }
{   CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON       }
{   ADDITIONAL RESTRICTIONS.                                        }
{                                                                   }
{*******************************************************************}
*/
#endregion Copyright (c) 2000-2008 Developer Express Inc.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using System.Threading;
using System.Windows.Media;
using System.Windows.Shapes;
using DevExpress.AgMenu.Utils;
using DevExpress.AgMenu.Internal;
namespace DevExpress.AgMenu.Internal {
	public interface IAgMenuScrollViewer {
		void MakeItemVisible(int index);
	}
	public interface IAgMenuScrollViewerChildrenOwner {
		void SetChild(IAgMenuScrollViewerContent content);
	}
	public interface IAgMenuScrollViewerContent {
		int ChildCount { get; }
		void AdjustHeightForFullyVisibleItems(ref double height);
		double GetChildHeight(int index);
	}
	public class AgMenuScrollViewer : ContentControl, IAgMenuScrollViewerChildrenOwner, IAgMenuScrollViewer {
		const string ElementBackwardButtonName = "BackwardButton";
		const string ElementContentPresenterName = "ContentPresenter";
		const string ElementForwardButtonName = "ForwardButton";
		public static readonly DependencyProperty ScrollButtonClickModeProperty = DependencyProperty.Register("ScrollButtonClickMode",
			typeof(AgMenuScrollButtonClickMode), typeof(AgMenuScrollViewer), null);
		IAgMenuScrollViewerContent children;
		int topIndex;
		public int TopIndex {
			get { return topIndex; }
			protected set {
				if (value > MaxTopIndex) value = MaxTopIndex;
				if (value < 0) value = 0;
				if (value == TopIndex) return;
				topIndex = value;
				PerformButtonsLayout();
				PerformContentLayout();
			}
		}
		int MaxTopIndex { get; set; }
		public AgMenuScrollButton BackwardButton { get; protected set; }
		public AgMenuScrollButton ForwardButton { get; protected set; }
		double GetBackwardButtonHeight(bool checkVisibility) {
			return (BackwardButton != null && (!checkVisibility || BackwardButton.GetVisible())) ? BackwardButton.Height : 0;
		}
		double GetForwardButtonHeight(bool checkVisibility) {
			return (ForwardButton != null && (!checkVisibility || ForwardButton.GetVisible())) ? ForwardButton.Height : 0;
		}
		AgMenuScrollContentPresenter ContentPresenter { get; set; }
		public AgMenuScrollButtonClickMode ScrollButtonClickMode {
			get { return (AgMenuScrollButtonClickMode)GetValue(ScrollButtonClickModeProperty); }
			set { SetValue(ScrollButtonClickModeProperty, value); }
		}
		public AgMenuScrollViewer() {
			DefaultStyleKey = typeof(AgMenuScrollViewer);
			ScrollButtonClickMode = AgMenuScrollButtonClickMode.Hover;
			TopIndex = 0;
			SizeChanged += new SizeChangedEventHandler(AgMenuScrollViewer_SizeChanged);
		}
		void AgMenuScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e) {
			PerformLayout();
		}
		private void CalcMaxTopIndex(bool useTopButton) {
			int lastIndex = Children.ChildCount - 1;
			double height = 0;
			double area = DesiredSize.Height - (useTopButton ? GetBackwardButtonHeight(true) : 0);
			while (height <= area && lastIndex >= 0) {
				height += Children.GetChildHeight(lastIndex);
				lastIndex--;
			}
			if (height > area) MaxTopIndex = lastIndex + 2;
			else MaxTopIndex = 0;
			if (MaxTopIndex != 0 && !useTopButton) CalcMaxTopIndex(true);
		}
		public override void OnApplyTemplate() {
			base.OnApplyTemplate();
			InitButtons();
			AssignContentPresenter();
			PerformLayout();
		}
		void AssignContentPresenter() {
			ContentPresenter = GetTemplateChild(ElementContentPresenterName) as AgMenuScrollContentPresenter;
		}
		void InitButtons() {
			AssignButtons();
			if (BackwardButton != null)
				BackwardButton.Click += new RoutedEventHandler(BackwardButton_Click);
			if (ForwardButton != null)
				ForwardButton.Click += new RoutedEventHandler(ForwardButton_Click);
		}
		protected virtual void AssignButtons() {
			BackwardButton = GetTemplateChild(ElementBackwardButtonName) as AgMenuScrollButton;
			ForwardButton = GetTemplateChild(ElementForwardButtonName) as AgMenuScrollButton;
		}
		void ForwardButton_Click(object sender, RoutedEventArgs e) {
			Scroll(1);
		}
		void BackwardButton_Click(object sender, RoutedEventArgs e) {
			Scroll(-1);
		}
		protected internal IAgMenuScrollViewerContent Children {
			get { return children; }
			set {
				if (value == children) return;
				children = value;
				PerformLayout();
			}
		}
		double GetChildrenTotalHeight(int firstIndex, int lastIndex) {
			double result = 0;
			for (int i = firstIndex; i <= lastIndex; i++) {
				result += Children.GetChildHeight(i);
			}
			return result;
		}
		public void PerformLayout() {
			if (Children == null) return;
			CalcMaxTopIndex(false);
			PerformButtonsLayout();
		}
		double GetScrollableAreaHeight() {
			return ActualHeight - GetBackwardButtonHeight(true) - GetForwardButtonHeight(true);
		}
		protected internal void PerformButtonsLayout() {
			if (BackwardButton != null) {
				BackwardButton.SetVisible(TopIndex != 0);
			}
			if (ForwardButton != null) {
				ForwardButton.SetVisible(TopIndex != MaxTopIndex);
			}
		}
		void PerformContentLayout() {
			if (ContentPresenter != null) {
				ContentPresenter.HorizontalOffset = 0;
				ContentPresenter.VerticalOffset = -GetChildrenTotalHeight(0, TopIndex - 1);
			}
		}
		internal void Scroll(int count) {
			TopIndex += count;
		}
		protected override Size MeasureOverride(Size availableSize) {
			if (!(Content is UIElement)) {
				return base.MeasureOverride(availableSize);
			}
			Size result = base.MeasureOverride(availableSize);
			result.Width = ((UIElement)VisualTreeHelper.GetChild(this, 0)).DesiredSize.Width;
			result.Height = (Content as UIElement).DesiredSize.Height;
			if (!Double.IsPositiveInfinity(availableSize.Height)) {
				if (result.Height > availableSize.Height) {
					double h = availableSize.Height - GetForwardButtonHeight(false);
					if (h > 0) {
						Children.AdjustHeightForFullyVisibleItems(ref h);
						result.Height = h + GetForwardButtonHeight(false);
						result = base.MeasureOverride(new Size(availableSize.Width, result.Height));
					} else {
						result.Height = availableSize.Height;
					}
				}
			}
			return result;
		}
		#region IAgMenuScrollViewerChildrenOwner Members
		public void SetChild(IAgMenuScrollViewerContent content) {
			Children = content;
		}
		#endregion
		#region IAgMenuScrollViewer Members
		void IAgMenuScrollViewer.MakeItemVisible(int index) {
			if (index < TopIndex)
				TopIndex = index;
			else
				if (ForwardButton.GetVisible()) {
					double h = GetChildrenTotalHeight(TopIndex, index) + GetForwardButtonHeight(false);
					if (TopIndex > 0)
						h += GetBackwardButtonHeight(false);
					if (h > ActualHeight) {
						h = ActualHeight - GetBackwardButtonHeight(false) - Children.GetChildHeight(index);
						if (index != Children.ChildCount - 1)
							h -= GetForwardButtonHeight(false);
						int newTopIndex = index;
						while (h > Children.GetChildHeight(newTopIndex)) {
							h -= Children.GetChildHeight(newTopIndex);
							newTopIndex--;
						}
						TopIndex = newTopIndex;
					}
				}
		}
		#endregion
	}
	public class AgMenuScrollButton : System.Windows.Controls.Button {
		public new static readonly DependencyProperty ClickModeProperty = DependencyProperty.Register("ClickMode", typeof(AgMenuScrollButtonClickMode),
			typeof(AgMenuScrollButton), null);
		public const int RepeatDelay = 5;
		public const int RepeatInterval = 100;
		int lostTicks = 0;
		DispatcherTimer repeater;
		public new AgMenuScrollButtonClickMode ClickMode {
			get { return (AgMenuScrollButtonClickMode)GetValue(ClickModeProperty); }
			set { SetValue(ClickModeProperty, value); }
		}
		public new Visibility Visibility {
			get { return base.Visibility; }
			set {
				base.Visibility = value;
				if (value == Visibility.Collapsed)
					EndRepeat();
			}
		}
		public AgMenuScrollButton() {
			base.ClickMode = System.Windows.Controls.ClickMode.Press;
			ClickMode = AgMenuScrollButtonClickMode.Hover;
			MouseEnter += AgRepeatButton_MouseEnter;
			MouseLeave += AgRepeatButton_MouseLeave;
		}
		public void SetVisible(bool value) {
			if (value)
				Visibility = Visibility.Visible;
			else
				Visibility = Visibility.Collapsed;
		}
		protected override void OnIsPressedChanged(DependencyPropertyChangedEventArgs e) {
			base.OnIsPressedChanged(e);
			if (ClickMode == AgMenuScrollButtonClickMode.Click) {
				if (IsPressed)
					BeginRepeat();
				else
					EndRepeat();
			}
		}
		void AgRepeatButton_MouseLeave(object sender, MouseEventArgs e) {
			if (ClickMode == AgMenuScrollButtonClickMode.Hover)
				EndRepeat();
		}
		void AgRepeatButton_MouseEnter(object sender, MouseEventArgs e) {
			if (ClickMode == AgMenuScrollButtonClickMode.Hover)
				BeginRepeat();
		}
		void BeginRepeat() {
			lostTicks = 0;
			repeater = new DispatcherTimer();
			repeater.Tick += Repeater_Tick;
			repeater.Interval = TimeSpan.FromMilliseconds(RepeatInterval);
			repeater.Start();
		}
		void EndRepeat() {
			if (repeater != null) {
				repeater.Stop();
				repeater = null;
			}
		}
		void DoClick(object state) {
			OnClick();
		}
		void Repeater_Tick(object sender, EventArgs e) {
			if (lostTicks < RepeatDelay) {
				lostTicks++;
				return;
			}
			DoClick(null);
		}
	}
	public class AgMenuScrollContentPresenter : ContentPresenter {
		double horizontalOffset;
		double prevActualHeight;
		double prevActualWidth;
		double verticalOffset;
		public double HorizontalOffset {
			get { return horizontalOffset; }
			set {
				if (value == horizontalOffset) return;
				horizontalOffset = value;
				InvalidateArrange();
			}
		}
		public double VerticalOffset {
			get { return verticalOffset; }
			set {
				if (value == verticalOffset) return;
				verticalOffset = value;
				InvalidateArrange();
				UpdateLayout();
			}
		}
		public AgMenuScrollContentPresenter() {
			LayoutUpdated += OnLayoutUpdated;
		}
		protected override Size ArrangeOverride(Size finalSize) {
			if (VisualTreeHelper.GetChildrenCount(this) != 0) {
				UIElement child = (UIElement)VisualTreeHelper.GetChild(this, 0);
				child.Arrange(new Rect(HorizontalOffset, VerticalOffset, finalSize.Width, child.DesiredSize.Height));
				child.UpdateLayout(); 
			}
			return finalSize;
		}
		protected override Size MeasureOverride(Size availableSize) {
			if (VisualTreeHelper.GetChildrenCount(this) == 0) {
				return base.MeasureOverride(availableSize);
			}
			UIElement child = (UIElement)VisualTreeHelper.GetChild(this, 0);
			child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
			Size result = child.DesiredSize;
			return result;
		}
		protected virtual void OnLayoutUpdated(object sender, EventArgs e) {
			if (prevActualWidth != ActualWidth || prevActualHeight != ActualHeight) {
				prevActualWidth = ActualWidth;
				prevActualHeight = ActualHeight;
				Clip = new RectangleGeometry() { Rect = new Rect(0, 0, ActualWidth, ActualHeight) };
			}
		}
	}
}
